﻿using System.Collections.Generic;
using System.Reflection;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Windows;
using System.Windows.Shapes;
using System.Windows.Media;
using System.Windows.Input;
using System.Linq;
using System.Diagnostics;
using System;
using System.Collections.ObjectModel;
using System.Collections;
using System.ComponentModel;
using System.Windows.Data;
using Microsoft.Research.DynamicDataDisplay.Charts.Navigation;

namespace Microsoft.Research.DynamicDataDisplay.Navigation
{
    /// <summary>
    /// Is responsible for displaying and populating default context menu of ChartPlotter
    /// </summary>
    public class DefaultContextMenu : IPlotterElement
    {
        private static readonly BitmapImage helpIcon;
        private static readonly BitmapImage copyScreenshotIcon;
        private static readonly BitmapImage saveScreenshotIcon;
        private static readonly BitmapImage fitToViewIcon;

        static DefaultContextMenu()
        {
            helpIcon = LoadIcon("HelpIcon");
            saveScreenshotIcon = LoadIcon("SaveIcon");
            copyScreenshotIcon = LoadIcon("CopyScreenshotIcon");
            fitToViewIcon = LoadIcon("FitToViewIcon");
        }

        private static BitmapImage LoadIcon(string name)
        {
            Assembly currentAssembly = typeof(DefaultContextMenu).Assembly;

            BitmapImage icon = new BitmapImage();
            icon.BeginInit();
            icon.StreamSource = currentAssembly.GetManifestResourceStream("Microsoft.Research.DynamicDataDisplay.Resources." + name + ".png");
            icon.EndInit();
            icon.Freeze();

            return icon;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DefaultContextMenu"/> class.
        /// </summary>
        public DefaultContextMenu()
        { }

        protected ContextMenu PopulateContextMenu(Plotter target)
        {
            ContextMenu menu = new ContextMenu();
            MenuItem fitToViewMenuItem = new MenuItem
            {
                Header = Strings.UIResources.ContextMenuFitToView,
                ToolTip = Strings.UIResources.ContextMenuFitToViewTooltip,
                Icon = new Image { Source = fitToViewIcon },
                Command = ChartCommands.FitToView,
                CommandTarget = target
            };

            MenuItem savePictureMenuItem = new MenuItem
            {
                Header = Strings.UIResources.ContextMenuSaveScreenshot,
                ToolTip = Strings.UIResources.ContextMenuSaveScreenshotTooltip,
                Icon = new Image { Source = saveScreenshotIcon },
                Command = ChartCommands.SaveScreenshot,
                CommandTarget = target
            };

            MenuItem copyPictureMenuItem = new MenuItem
            {
                Header = Strings.UIResources.ContextMenuCopyScreenshot,
                ToolTip = Strings.UIResources.ContextMenuCopyScreenshotTooltip,
                Icon = new Image { Source = copyScreenshotIcon },
                Command = ChartCommands.CopyScreenshot,
                CommandTarget = target
            };

            MenuItem quickHelpMenuItem = new MenuItem
            {
                Header = Strings.UIResources.ContextMenuQuickHelp,
                ToolTip = Strings.UIResources.ContextMenuQuickHelpTooltip,
                Command = ChartCommands.ShowHelp,
                Icon = new Image { Source = helpIcon },
                CommandTarget = target
            };

            //MenuItem reportFeedback = new MenuItem
            //{
            //	Header = Strings.UIResources.ContextMenuReportFeedback,
            //	ToolTip = Strings.UIResources.ContextMenuReportFeedbackTooltip,
            //	Icon = (Image)plotter.Resources["SendFeedbackIcon"]
            //};
            //reportFeedback.Click += reportFeedback_Click;

            staticMenuItems.Add(fitToViewMenuItem);
            staticMenuItems.Add(copyPictureMenuItem);
            staticMenuItems.Add(savePictureMenuItem);
            staticMenuItems.Add(quickHelpMenuItem);
            //staticMenuItems.Add(reportFeedback);

            menu.ItemsSource = staticMenuItems;

            return menu;
        }

        private void reportFeedback_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                using (Process.Start("mailto:" + Strings.UIResources.SendFeedbackEmail + "?Subject=[D3]%20" + typeof(DefaultContextMenu).Assembly.GetName().Version)) { }
            }
            catch (Exception)
            {
                MessageBox.Show(Strings.UIResources.SendFeedbackError + Strings.UIResources.SendFeedbackEmail, "Error while sending feedback", MessageBoxButton.OK, MessageBoxImage.Information);
            }
        }

        private readonly ObservableCollection<object> staticMenuItems = new ObservableCollection<object>();

        // hidden because default menu items' command target is plotter, and serializing this will
        // cause circular reference

        /// <summary>
        /// Gets the static menu items.
        /// </summary>
        /// <value>The static menu items.</value>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public ObservableCollection<object> StaticMenuItems
        {
            get { return staticMenuItems; }
        }

        #region IPlotterElement Members

        private Plotter2D plotter;

        void IPlotterElement.OnPlotterAttached(Plotter plotter)
        {
            this.plotter = (Plotter2D)plotter;

            ContextMenu menu = PopulateContextMenu(plotter);
            plotter.ContextMenu = menu;

            plotter.PreviewMouseRightButtonDown += plotter_PreviewMouseRightButtonDown;
            plotter.PreviewMouseRightButtonUp += plotter_PreviewMouseRightButtonUp;
            plotter.PreviewMouseLeftButtonDown += plotter_PreviewMouseLeftButtonDown;

            plotter.ContextMenu.Closed += ContextMenu_Closed;
        }

        private void plotter_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            // this will prevent other tools like PointSelector from
            // wrong actuations
            if (contextMenuOpen)
            {
                plotter.Focus();
                e.Handled = true;
            }
        }

        void IPlotterElement.OnPlotterDetaching(Plotter plotter)
        {
            plotter.ContextMenu.Closed -= ContextMenu_Closed;

            plotter.ContextMenu = null;
            plotter.PreviewMouseRightButtonDown -= plotter_PreviewMouseRightButtonDown;
            plotter.PreviewMouseRightButtonUp -= plotter_PreviewMouseRightButtonUp;

            this.plotter = null;
        }

        private void ContextMenu_Closed(object sender, RoutedEventArgs e)
        {
            contextMenuOpen = false;
            foreach (var item in dynamicMenuItems)
            {
                staticMenuItems.Remove(item);
            }
        }

        private Point mousePos;

        /// <summary>
        /// Gets the mouse position when right mouse button was clicked.
        /// </summary>
        /// <value>The mouse position on click.</value>
        public Point MousePositionOnClick
        {
            get { return mousePos; }
        }

        private void plotter_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            contextMenuOpen = false;
            mousePos = e.GetPosition(plotter);
        }

        private bool contextMenuOpen = false;
        private readonly ObservableCollection<object> dynamicMenuItems = new ObservableCollection<object>();

        private void plotter_PreviewMouseRightButtonUp(object sender, MouseButtonEventArgs e)
        {
            Point position = e.GetPosition(plotter);
            if (mousePos == position)
            {
                hitResults.Clear();
                VisualTreeHelper.HitTest(plotter, null, CollectAllVisuals_Callback, new PointHitTestParameters(position));

                foreach (var item in dynamicMenuItems)
                {
                    staticMenuItems.Remove(item);
                }
                dynamicMenuItems.Clear();
                var dynamicItems = (hitResults.Where(r =>
                {
                    IPlotterContextMenuSource menuSource = r as IPlotterContextMenuSource;
                    if (menuSource != null)
                    {
                        menuSource.BuildMenu();
                    }

                    var items = GetPlotterContextMenu(r);
                    return items != null && items.Count > 0;
                }).SelectMany(r =>
                {
                    var menuItems = GetPlotterContextMenu(r);

                    FrameworkElement chart = r as FrameworkElement;
                    if (chart != null)
                    {
                        foreach (var menuItem in menuItems.OfType<MenuItem>())
                        {
                            menuItem.DataContext = chart.DataContext;
                        }
                    }
                    return menuItems;
                })).ToList();

                foreach (var item in dynamicItems)
                {
                    dynamicMenuItems.Add(item);
                    //MenuItem menuItem = item as MenuItem;
                    //if (menuItem != null)
                    //{
                    //    menuItem.CommandTarget = plotter;
                    //}
                }

                staticMenuItems.AddMany(dynamicMenuItems);

                plotter.Focus();
                contextMenuOpen = true;

                // in XBAP applications these lines throws a SecurityException, as (as I think)
                // we are opening "new window" here. So in XBAP we are not opening context menu manually, but
                // it will be opened by itself as this is right click.
#if !RELEASEXBAP
                plotter.ContextMenu.IsOpen = true;
                e.Handled = true;
#endif
            }
            else
            {
                // this is to prevent showing menu when RMB was pressed, then moved and now is releasing.
                e.Handled = true;
            }
        }

        #region PlotterContextMenu property

        /// <summary>
        /// Gets the plotter context menu.
        /// </summary>
        /// <param name="obj">The obj.</param>
        /// <returns></returns>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public static ObjectCollection GetPlotterContextMenu(DependencyObject obj)
        {
            return (ObjectCollection)obj.GetValue(PlotterContextMenuProperty);
        }

        /// <summary>
        /// Sets the plotter context menu.
        /// </summary>
        /// <param name="obj">The obj.</param>
        /// <param name="value">The value.</param>
        public static void SetPlotterContextMenu(DependencyObject obj, ObjectCollection value)
        {
            obj.SetValue(PlotterContextMenuProperty, value);
        }

        /// <summary>
        /// Identifies the PlotterContextMenu attached property.
        /// </summary>
        public static readonly DependencyProperty PlotterContextMenuProperty = DependencyProperty.RegisterAttached(
          "PlotterContextMenu",
          typeof(ObjectCollection),
          typeof(DefaultContextMenu),
          new FrameworkPropertyMetadata(null));

        #endregion PlotterContextMenu property

        private List<DependencyObject> hitResults = new List<DependencyObject>();

        private HitTestResultBehavior CollectAllVisuals_Callback(HitTestResult result)
        {
            if (result == null || result.VisualHit == null)
                return HitTestResultBehavior.Stop;

            hitResults.Add(result.VisualHit);
            return HitTestResultBehavior.Continue;
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        Plotter IPlotterElement.Plotter
        {
            get { return plotter; }
        }

        #endregion IPlotterElement Members
    }

    /// <summary>
    /// Represents a collection of additional menu items in ChartPlotter's context menu.
    /// </summary>
    public sealed class ObjectCollection : ObservableCollection<Object>
    { }
}